#include "Spark/Utils/Logger.h"
#include <array>
#include <unistd.h>
#include <assert.h>
#include <sys/syscall.h>

const char *strerror_tl(int savedErrno)
{
    return strerror(savedErrno);
}

namespace wb
{
OutputFunc Logger::outputFunc_ = &Logger::defaultOutputFunction;
FlushFunc Logger::flushFunc_ = &Logger::defaultFlushFunction;

static const char *levelstr[static_cast<size_t>(
    Logger::LogLevel::kNumberOfLogLevels)] = {" [TRACE] ",
                                              " [DEBUG] ",
                                              " [INFO@] ",
                                              " [WARN!] ",
                                              " [ERROR] ",
                                              " [FATAL] "};

inline LogStream &operator<<(LogStream &s, Logger::LogLevel level)
{
    const char *str = levelstr[static_cast<size_t>(level)];
    size_t len = 9;
    s.append(str, len);
    return s;
}

inline LogStream &operator<<(LogStream &s, const Logger::SourceFile &v)
{
    s.append(v.data_, v.size_);
    return s;
}

static thread_local time_t lastSecond_{0};
static thread_local std::array<char, 32> lastTimeString_{0};
static thread_local pid_t threadId_{0};

void Logger::formatTime()
{
    auto nowSec = time(nullptr);
    auto microSec = (date_.toMicroseconds() - date_.toSeconds()).count();
    if (nowSec != lastSecond_)
    {
        TimePoint::formatStr(lastTimeString_.data(),
                             lastTimeString_.size(),
                             nowSec);
    }
    logStream_ << lastTimeString_.data();
    char tmp[32];
    snprintf(tmp, sizeof(tmp), ".%06ld ", microSec);
    logStream_.append(tmp, 8);
    if (threadId_ == 0)
        threadId_ = static_cast<pid_t>(::syscall(SYS_gettid));
    logStream_ << threadId_;
}

Logger::Logger(SourceFile file, int line)
    : sourceFile_(file), fileLine_(line), level_(LogLevel::kInfo)
{
    formatTime();
    logStream_ << level_;
}

Logger::Logger(SourceFile file, int line, LogLevel level)
    : sourceFile_(file), fileLine_(line), level_(level)
{
    formatTime();
    logStream_ << level_;
}

Logger::Logger(SourceFile file, int line, LogLevel level, const char *func)
    : sourceFile_(file), fileLine_(line), level_(level)
{
    formatTime();
    logStream_ << level_ << "[" << func << "] ";
}

Logger::Logger(SourceFile file, int line, bool)
    : sourceFile_(file), fileLine_(line), level_(LogLevel::kFatal)
{
    formatTime();
    logStream_ << level_;
    if (errno != 0)
    {
        logStream_ << strerror_tl(errno) << " (errno=" << errno << ") ";
    }
}

Logger::~Logger()
{
    logStream_ << " - " << sourceFile_ << ':' << fileLine_ << '\n';
    auto &oFunc = Logger::outputFunc_;
    if (!oFunc)
        return;
    oFunc(logStream_.bufferData(), logStream_.bufferLength());
    if (level_ >= LogLevel::kError)
        Logger::flushFunc_();
}

LogStream &Logger::stream()
{
    return logStream_;
}

}  // namespace wb